{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Disco Tutorials"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Disco Diffusion is a colab work cooperated by many talented people. From the algorithm aspect, this work is based on guided-diffusion and clip-guided diffusion. \n",
    "\n",
    "The generation process of disco-diffusion consists of following steps,\n",
    "1. Disco-diffusion takes as inputs user-provided text prompts and image prompts.\n",
    "2. A pretrained clip model calculates the embeddings of the text and image prompts.\n",
    "3. The Unet will predict the original sample.\n",
    "3. A list of image cutouts will be generated from original sample by a cutter.\n",
    "4. The generation process is optimized by calculating the similarity between the embeddings of the image cutouts, text and image prompts, and other losses.\n",
    "5. Finally, with the carefully-designed loss functions, the model enables classifier-guidance samping to generate desired images according to the text and image prompts.\n",
    "\n",
    "So corresponding to above processes, we will introduce what to set for generating and show your results by adjusting arguments/ configs. \n",
    "\n",
    "The contents of this tutorials are as follows:\n",
    "\n",
    "[1.Runtime Settings](#1-Runtime-Settings)\n",
    "\n",
    "[2.Unet Settings](#2-Unet-Settings)\n",
    "\n",
    "[3.CLIP Models Settings](#3-CLIP-Models-Settings)\n",
    "\n",
    "[4.Diffusion Scheduler Settings](#4-Diffusion-Scheduler-Settings)\n",
    "\n",
    "[5.Loss Settings](#5-Loss-Settings)\n",
    "\n",
    "[6.Cutter Settings](#6-Cutter-Settings)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Install MMagic"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Check PyTorch version and CLIP version\n",
    "!pip list | grep torch\n",
    "!pip list | grep clip"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install mmcv dependency via openmim\n",
    "!pip install openmim\n",
    "!mim install 'mmcv>=2.0.0'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install mmagic from source\n",
    "%cd /content/\n",
    "!rm -rf mmagic\n",
    "!git clone https://github.com/open-mmlab/mmagic.git \n",
    "%cd mmagic\n",
    "!pip install -r requirements.txt\n",
    "!pip install -e ."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Runtime Settings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "from mmengine import Config, MODELS\n",
    "from mmengine.registry import init_default_scope\n",
    "from mmagic.registry import MODULES\n",
    "from mmcv import tensor2imgs\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "from torchvision.transforms import ToPILImage, Normalize, Compose\n",
    "from IPython.display import Image\n",
    "\n",
    "init_default_scope('mmagic')\n",
    "\n",
    "\n",
    "def show_tensor(image_tensor, index=0):\n",
    "    normalized_image = ((image_tensor + 1.) / 2.).clamp(0, 1)\n",
    "    out = tensor2imgs(normalized_image * 255, to_rgb=False)\n",
    "    plt.imshow(out[index])\n",
    "    plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "config = 'configs/disco_diffusion/disco-diffusion_adm-u-finetuned_imagenet-512x512.py'\n",
    "disco = MODELS.build(Config.fromfile(config).model).cuda().eval()\n",
    "text_prompts = {\n",
    "    0: [\"clouds surround the mountains and Chinese palaces, sunshine, lake, overlook, overlook, unreal engine, light effect, Dream, Greg Rutkowski, James Gurney, artstation\"]\n",
    "}\n",
    "\n",
    "seed = 2022\n",
    "num_inference_steps = 250\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, seed=seed)['samples']\n",
    "show_tensor(image)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Image Resolution\n",
    "Despite the limit of your device limitation, you can set height and width of image as you like."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# image resolution\n",
    "image = disco.infer(width=768, height=1280, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, seed=seed)['samples']\n",
    "show_tensor(image)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### image_prompts\n",
    " Work in progress."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# # image prompts\n",
    "# image_prompts = ['path_of_image']\n",
    "# image = disco.infer(width=768, height=1280, image_prompts=image_prompts, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, seed=seed)['samples']\n",
    "# show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### clip_guidance_scale\n",
    "clip_guidance_scale is one of the most important parameters you will  use. It tells DD how strongly you want CLIP to move toward your prompt  each timestep.  Higher is generally better, but if CGS is too strong it  will overshoot the goal and distort the image. So a happy medium is  needed, and it takes experience to learn how to adjust CGS."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# clip guidance scale\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, clip_guidance_scale=8000, seed=seed)['samples']\n",
    "show_tensor(image)\n",
    "\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, clip_guidance_scale=4000, seed=seed)['samples']\n",
    "show_tensor(image)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Unet Settings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Disco-Diffusion provides different unet, and we offer configs for the different unets and convert the weights. You only need to select these configs to use the different unets freely."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 256x256_diffusion_uncond\n",
    "text_prompts = {\n",
    "    0: [\"clouds surround the mountains and Chinese palaces,sunshine,lake,overlook,overlook,unreal engine,light effect,Dream，Greg Rutkowski,James Gurney,artstation\"]\n",
    "}\n",
    "config = 'configs/disco_diffusion/disco-diffusion_adm-u-finetuned_imagenet-256x256.py'\n",
    "disco = MODELS.build(Config.fromfile(config).model).cuda().eval()\n",
    "image = disco.infer(width=512, height=448, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, seed=seed)['samples']\n",
    "show_tensor(image)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# 512x512_diffusion_uncond_finetune_008100\n",
    "config = 'configs/disco_diffusion/disco-diffusion_adm-u-finetuned_imagenet-512x512.py'\n",
    "disco = MODELS.build(Config.fromfile(config).model).cuda().eval()\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# portrait_generator_v001\n",
    "text_prompts = {\n",
    "    0: [\"a portrait of supergirl, by artgerm, rosstran, trending on artstation.\"]\n",
    "}\n",
    "config = 'disco-diffusion_portrait-generator-v001.py'\n",
    "disco = MODELS.build(Config.fromfile(config).model).cuda().eval()\n",
    "image = disco.infer(width=512, height=512, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, tv_scale=5000, range_scale = 5000, sat_scale = 15250, clip_guidance_scale=20000, seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The rest unets will come soon!\n",
    "- pixelartdiffusion_expanded\n",
    "- pixel_art_diffusion_hard_256\n",
    "- pixelartdiffusion4k\n",
    "- watercolordiffusion_2\n",
    "- watercolordiffusion\n",
    "- PulpSciFiDiffusion\n",
    "- secondary\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.CLIP Models Settings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Disco-Diffusion uses different clip models to guide the image generation, and the images generated by different clip models have different characteristics. In practice, we combine multiple clip models to generate images.\n",
    " In order to study the effect of different clip models, in the following example, we only use one clip model at a time, with other settings keeping the same,  you can experience the characteristics of different clip models by observing the results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from mmagic.models.editors.disco_diffusion.guider import ImageTextGuider\n",
    "\n",
    "\n",
    "config = 'configs/disco_diffusion/disco-diffusion_adm-u-finetuned_imagenet-512x512.py'\n",
    "disco = MODELS.build(Config.fromfile(config).model).cuda().eval()\n",
    "text_prompts = {0: [\"A beautiful painting of a map of the city of Atlantis\"]}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### ViTB32\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(type='ClipWrapper', clip_type='clip', name='ViT-B/32', jit=False),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ViTB16"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(type='ClipWrapper', clip_type='clip', name='ViT-B/16', jit=False),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ViTL14"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(type='ClipWrapper', clip_type='clip', name='ViT-L/14', jit=False),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ViTL14_336px"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper', clip_type='clip', name='ViT-L/14@336px',\n",
    "        jit=False),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RN50"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(type='ClipWrapper', clip_type='clip', name='RN50', jit=False),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RN50x4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(type='ClipWrapper', clip_type='clip', name='RN50x4', jit=False),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RN50x16"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(type='ClipWrapper', clip_type='clip', name='RN50x16', jit=False),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RN50x64"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(type='ClipWrapper', clip_type='clip', name='RN50x64', jit=False),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RN101"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(type='ClipWrapper', clip_type='clip', name='RN101', jit=False),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ViTB32_laion2b_e16"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='ViT-B-32',\n",
    "        pretrained='laion2b_e16',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ViTB32_laion400m_e31"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='ViT-B-32',\n",
    "        pretrained='laion400m_e31',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ViTB32_laion400m_32"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='ViT-B-32',\n",
    "        pretrained='laion400m_e32',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ViTB32quickgelu_laion400m_e31"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='ViT-B-32-quickgelu',\n",
    "        pretrained='laion400m_e31',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ViTB32quickgelu_laion400m_e32"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='ViT-B-32-quickgelu',\n",
    "        pretrained='laion400m_e32',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ViTB16_laion400m_e31"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='ViT-B-16',\n",
    "        pretrained='laion400m_e31',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ViTB16_laion400m_e32"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='ViT-B-16',\n",
    "        pretrained='laion400m_e32',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RN50_yffcc15m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='RN50',\n",
    "        pretrained='yfcc15m',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RN50_cc12m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='RN50',\n",
    "        pretrained='cc12m',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RN50_quickgelu_yfcc15m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='RN50-quickgelu',\n",
    "        pretrained='yfcc15m',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RN50_quickgelu_cc12m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='RN50-quickgelu',\n",
    "        pretrained='cc12m',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RN101_yfcc15m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='RN101',\n",
    "        pretrained='yfcc15m',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RN101_quickgelu_yfcc15m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "clip_models = []\n",
    "clip_models_cfg = [\n",
    "    dict(\n",
    "        type='ClipWrapper',\n",
    "        clip_type='open_clip',\n",
    "        model_name='RN101-quickgelu',\n",
    "        pretrained='yfcc15m',\n",
    "        device='cuda'),\n",
    "]\n",
    "for clip_cfg in clip_models_cfg:\n",
    "    clip_models.append(MODULES.build(clip_cfg))\n",
    "disco.guider = ImageTextGuider(clip_models).cuda()\n",
    "\n",
    "image = disco.infer(\n",
    "    width=1280,\n",
    "    height=768,\n",
    "    text_prompts=text_prompts,\n",
    "    show_progress=True,\n",
    "    num_inference_steps=num_inference_steps,\n",
    "    eta=0.8,\n",
    "    seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4.Diffusion Scheduler Settings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Typically, a diffusion model generates images by a reverse scheduler. Many researchers are working on designing different reverse schedulers to improve diffusion models. Now, we only support DDIM scheduler. More kinds of reverse schedulers are coming soon."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### skip_steps\n",
    "First, When you set the value of `init_image`, you can adjust `skip_steps` for creative reasons. With low skip_steps you can get a result “inspired by” the init_image which will retain the colors and rough layout and shapes but look quite different. With high skip_steps you can preserve most of the init_image contents and only fine-tune the texture."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!wget https://user-images.githubusercontent.com/22982797/205579254-30c3b446-63bb-4172-bbfe-8d1d05e151cf.png -O init.png"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "init_path = 'init.png'\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, init_image=init_path, skip_steps=50, seed=seed)['samples']\n",
    "show_tensor(image)\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, init_image=init_path, skip_steps=200, seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that, you can still use `skip_steps` to reduce rendering time even you don't set the value of `init_image`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### steps\n",
    "Increasing steps will provide more opportunities for the AI to adjust the image, and each adjustment will be smaller, and thus will yield a  more precise, detailed image. Using a larger `steps` will generally increase image quality but also increasing the render time. However, some intricate images can take 1000, 2000, or more steps. It is really up to the user.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "steps = 100\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=steps, eta=0.8, seed=seed)['samples']\n",
    "show_tensor(image)\n",
    "\n",
    "steps = 1000\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=steps, eta=0.8, seed=seed)['samples']\n",
    "show_tensor(image)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.Loss Settings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We define `loss_cfg` in config like this. The loss function defines the distance between the generated image and the expected target, so you can adjust loss settings for you purpose."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "loss_cfg = dict(tv_scale=0, range_scale=150, sat_scale=0, init_scale=1000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "config = 'configs/disco_diffusion/disco-diffusion_adm-u-finetuned_imagenet-512x512.py'\n",
    "disco = MODELS.build(Config.fromfile(config).model).cuda().eval()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### range_scale\n",
    "Optional, set to zero to turn off. It is used to adjust color contrast. Lower range_scale will increase contrast. Very low numbers  create a reduced color palette, resulting in more vibrant or poster-like  images. Higher range_scale will reduce contrast, for more muted images."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# range_scale\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, range_scale=50, seed=seed)['samples']\n",
    "show_tensor(image)\n",
    "\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, range_scale=200, seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### tv_scale\n",
    "Total variance denoising. Optional, set to zero to turn off. Controls  ‘smoothness’ of final output. If used, tv_scale will try to smooth out  your final image to reduce overall noise. If your image is too 'crunchy', increase tv_scale. TV denoising is good at preserving edges while smoothing away noise in flat regions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# tv_scale\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, tv_scale=0.1, seed=seed)['samples']\n",
    "show_tensor(image)\n",
    "\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, tv_scale=0.9, seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### sat_scale\n",
    "Saturation scale. Optional, set to zero to turn off.   If used, sat_scale will help mitigate oversaturation. If your image is  too saturated, increase sat_scale to reduce saturation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# sat_scale\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, sat_scale=0.1, seed=seed)['samples']\n",
    "show_tensor(image)\n",
    "\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, sat_scale=0.9, seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### init_scale\n",
    " This controls how strongly CLIP will try to match the  init_image provided.  This is balanced against  the clip_guidance_scale (CGS) above. An extreamly large value of `init_scale` won't change the results dramatcally, while an exteamly large value of CGS will destroy the `init_image`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# init_scale\n",
    "init_path = 'init.png'\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, init_image=init_path, skip_steps=125, init_scale=1000, seed=seed)['samples']\n",
    "show_tensor(image)\n",
    "\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, init_image=init_path, skip_steps=125, init_scale=100, seed=seed)['samples']\n",
    "show_tensor(image)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6.Cutter Settings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Cutter Settings\n",
    "This section determines the schedule of CLIP cuts, or snapshots that  CLIP uses to evaluate your image while processing.  In DD, there are two  types of cuts: overview cuts, which take a snapshot of the entire image and evaluate that against the prompt, and inner cuts,  which are smaller cropped images from the interior of the image,  helpful in tuning fine details.  The size of the inner cuts can be  adjusted using the cut_ic_pow parameter."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### cut_overview\n",
    "The schedule of overview cuts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# cut_overview\n",
    "cut_overview = [12] * 100 + [4] * 900\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, cut_overview=cut_overview, seed=seed)['samples']\n",
    "show_tensor(image)\n",
    "\n",
    "cut_overview = [12] * 900 + [4] * 100\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, cut_overview=cut_overview, seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### cut_innercut\n",
    "The schedule of inner cuts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# cut_innercut\n",
    "cut_innercut = [4] * 100 + [12] * 900\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, cut_innercut=cut_innercut, seed=seed)['samples']\n",
    "show_tensor(image)\n",
    "\n",
    "cut_innercut = [4] * 900 + [12] * 100\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, cut_innercut=cut_innercut, seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### cut_ic_pow\n",
    "This sets the size of the border used for inner cuts.   High cut_ic_pow values have larger borders, and therefore the cuts  themselves will be smaller and provide finer details.  If you have too  many or too-small inner cuts, you may lose overall image coherency  and/or it may cause an undesirable ‘mosaic’ effect.    Low cut_ic_pow values will allow the inner cuts to be larger, helping  image coherency while still helping with some details."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# cut_ic_pow\n",
    "cut_ic_pow = [1] * 200 + [0] * 800\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, cut_ic_pow=cut_ic_pow, seed=seed)['samples']\n",
    "show_tensor(image)\n",
    "\n",
    "cut_ic_pow = [1] * 800 + [0] * 200\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, cut_ic_pow=cut_ic_pow, seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### cut_icgray_p\n",
    "In addition to the overall cut schedule, a portion of the cuts can be  set to be grayscale instead of color.   This may help with improved  definition of shapes and edges, especially in the early diffusion steps  where the image structure is being defined.  cut_icgray_p affects  overview and inner cuts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# cut_icgray_p\n",
    "cut_icgray_p=[0.2] * 200 + [0] * 800\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, cut_icgray_p=cut_icgray_p, seed=seed)['samples']\n",
    "show_tensor(image)\n",
    "\n",
    "cut_icgray_p=[0.2] * 800 + [0] * 200\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, cut_icgray_p=cut_icgray_p, seed=seed)['samples']\n",
    "show_tensor(image)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### cutn_batches\n",
    "Each iteration, the AI cuts the image into smaller  pieces known as cuts, and compares each cut to the prompt to decide how  to guide the next diffusion step.  More cuts can generally lead to  better images, since DD has more chances to fine-tune the image  precision at each timesteps.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# cutn_batches\n",
    "cutn_batches = 2\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, cutn_batches=cutn_batches, seed=seed)['samples']\n",
    "show_tensor(image)\n",
    "\n",
    "cutn_batches = 8\n",
    "image = disco.infer(width=1280, height=768, text_prompts=text_prompts, show_progress=True, num_inference_steps=num_inference_steps, eta=0.8, cutn_batches=cutn_batches, seed=seed)['samples']\n",
    "show_tensor(image)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.9.16 ('mmedit': conda)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.16"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "35fd9fbe4d1b297d4be2b8092ffedf61ac9a34aef1ae0231052d90ec0a3e8f9f"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
